package w83a.util.jdbc;

import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;

import w83a.w83aBeans.W83aUsuarioXLNetsBean;
import w83a.w83aDAO.w83aConstantesBD.W83aBaseBeanBD;
import w83a.w83aUtilidadesComunes.W83aClsConstantes;
import w83a.w83aUtilidadesComunes.W83aClsTrazas;
import es.ejie.frmk.listeners.base.Q70ListenerUtils;

// TODO: Auto-generated Javadoc
/**
 * The Class U66aDataBase.
 *
 * @author Deusto Sistemas
 */
public abstract class W83aDataBase {
	
	/**
	 * Instantiates a new u66a data base.
	 */
	public W83aDataBase() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * Gets the connection.
	 *
	 * @return the connection
	 * @throws NamingException the naming exception
	 * @throws SQLException the sQL exception
	 */
	public static Connection getConnection() throws NamingException, SQLException {
		Connection con; // NOPMD
		Hashtable ht=new Hashtable(); 
		ht.put(Context.INITIAL_CONTEXT_FACTORY,Q70ListenerUtils.getApplicationProperty("INITIAL_CONTEXT_FACTORY")); 
		  Context ctx=new InitialContext(ht);
		  DataSource ds=(DataSource)ctx.lookup(Q70ListenerUtils.getApplicationProperty("DATA_SOURCE"));
		  con=ds.getConnection();
		return con;
	}
	
	/**
	 * Execute query.
	 *
	 * @throws Exception the exception
	 */
	public static List executeQuery(String sql, List parametros, Class bean, W83aUsuarioXLNetsBean userBean,Object otrosValores) throws Exception {
		
		W83aClsTrazas.formatTraza(userBean,"query->"+sql);
		W83aClsTrazas.formatTraza(userBean,"Parámetros->"+parametros.toString());	
		
		long milisegundos = System.currentTimeMillis();
		
		Connection con = null; //NOPMD
		List resultado = new ArrayList();
		con = getConnection();
	    PreparedStatement st=con.prepareStatement(sql);
	    if (parametros!=null && parametros.size()>0){
	    	for (int i=0;i<parametros.size();i++){
	    		st.setObject(i+1, parametros.get(i));
	    	}
	    }
	    
	    ResultSet res=st.executeQuery(); //NOPMD
	    resultado = getRSData(res,bean,otrosValores);
	    	    
	    st.close();
	    con.close();
		    
		W83aClsTrazas.formatTraza(userBean,"Query OK -> "+ (System.currentTimeMillis()- milisegundos) + " ms");	    
	    
	    return resultado;		
	}
	
	/**
	 * Execute query.
	 *
	 * @throws Exception the exception
	 */
	public static HashMap executeQueryHashMap(String sql, List parametros, Class bean, W83aUsuarioXLNetsBean userBean,Object otrosValores,String clave) throws Exception {//NOPMD
		
		W83aClsTrazas.formatTraza(userBean,"query->"+sql);
		W83aClsTrazas.formatTraza(userBean,"Parámetros->"+parametros.toString());	
		
		long milisegundos = System.currentTimeMillis();
		
		Connection con = null;//NOPMD
		HashMap resultado = new HashMap();
		con = getConnection();
		PreparedStatement st=con.prepareStatement(sql);
		if (parametros!=null && parametros.size()>0){
			for (int i=0;i<parametros.size();i++){
				st.setObject(i+1, parametros.get(i));
			}
		}
		
			
		ResultSet res=st.executeQuery(); //NOPMD
		clave = clave.substring(0,1).toUpperCase() + clave.substring(1);
		resultado = getRSDataHashMap(res,bean,otrosValores,clave);
		st.close();
		con.close();
		
		W83aClsTrazas.formatTraza(userBean,"Query OK -> "+ (System.currentTimeMillis()- milisegundos) + " ms");	    
		
		return resultado;		
	}
	
	/**
	 * Execute query.
	 *
	 * @throws Exception the exception
	 */
	public static List executeQuery(String sql, List parametros,W83aUsuarioXLNetsBean usuarioBean) throws Exception {
		
		W83aClsTrazas.formatTraza(usuarioBean,"query->"+sql);
		W83aClsTrazas.formatTraza(usuarioBean,"Parámetros->"+parametros.toString());
		
		long milisegundos = System.currentTimeMillis();
		
		Connection con = null; // NOPMD
		con = getConnection();
		PreparedStatement st=con.prepareStatement(sql);
		if (parametros!=null && parametros.size()>0){
			for (int i=0;i<parametros.size();i++){
				st.setObject(i+1, parametros.get(i));
			}
		}
		ResultSet res=st.executeQuery(); // NOPMD
		ResultSetMetaData rsMetaData = res.getMetaData();
	    int numberOfColumns = rsMetaData.getColumnCount();
		
		List listaObjetos = new ArrayList();
		
		while(res.next()){
			HashMap mapaFila = new HashMap();//NOPMD
			for (int i = 1; i < numberOfColumns + 1; i++) {
			   String columnName = rsMetaData.getColumnName(i);
			   // Get the name of the column's table name
			   mapaFila.put(columnName, res.getObject(columnName));
			}
			listaObjetos.add(mapaFila);			      
		}
		st.close();
		con.close();
		
		W83aClsTrazas.formatTraza(usuarioBean,"Query OK -> "+ (System.currentTimeMillis()- milisegundos) + " ms");	    
		
		return listaObjetos;		
	}
	
	/** Ejecuta una consulta que solo tiene una columna
	 * Execute query.
	 *
	 * @throws Exception the exception
	 */
	public static List executeSimpleQuery(String sql, List parametros,W83aUsuarioXLNetsBean usuarioBean) throws Exception {
		W83aClsTrazas.formatTraza(usuarioBean,"query->"+sql);
		W83aClsTrazas.formatTraza(usuarioBean,"Parámetros->"+parametros.toString());
		
		long milisegundos = System.currentTimeMillis();
		
		Connection con = null; // NOPMD
		
		con = getConnection();
		PreparedStatement st=con.prepareStatement(sql);
		if (parametros!=null && parametros.size()>0){
			for (int i=0;i<parametros.size();i++){
				st.setObject(i+1, parametros.get(i));
			}
		}
		ResultSet res=st.executeQuery(); // NOPMD 
		
		List listaObjetos = new ArrayList();
		
		while(res.next()){
			listaObjetos.add(res.getObject(1));
		}
		st.close();
		con.close();
		
		W83aClsTrazas.formatTraza(usuarioBean,"Query OK -> "+ (System.currentTimeMillis()- milisegundos) + " ms");	    
		
		return listaObjetos;		
	}
	
	/** Ejecuta una consulta que solo tiene una columna
	 * Execute query.
	 *
	 * @throws Exception the exception
	 */
	public static List executeSimpleQuerySinTrazas(String sql, List parametros,W83aUsuarioXLNetsBean usuarioBean) throws Exception {
		
		Connection con = null; // NOPMD 
		con = getConnection();
		PreparedStatement st=con.prepareStatement(sql);
		if (parametros!=null && parametros.size()>0){
			for (int i=0;i<parametros.size();i++){
				st.setObject(i+1, parametros.get(i));
			}
		}
		ResultSet res=st.executeQuery(); // NOPMD b
		
		List listaObjetos = new ArrayList();
		
		while(res.next()){
			listaObjetos.add(res.getObject(1));
		}
		st.close();
		con.close();	
		
		return listaObjetos;		
	}

	
	/**
	 * Gets the rS data.
	 *
	 * @param res the res
	 * @return the rS data
	 * @throws SQLException the sQL exception
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	private static HashMap getRSDataHashMap(ResultSet res, Class clase, Object otrosValores,String clave) throws Exception {
		HashMap lista = new HashMap();
		while (res.next()){		    
			W83aBaseBeanBD bean = (W83aBaseBeanBD)clase.newInstance();
			bean.rowToBean(res,otrosValores);
			lista.put(bean.getClass().getMethod("get"+clave, null).invoke(bean, null),bean);
		}
		
		return lista;
	}

	/**
	 * Gets the rS data.
	 *
	 * @param res the res
	 * @return the rS data
	 * @throws SQLException the sQL exception
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	private static List getRSData(ResultSet res, Class clase, Object otrosValores) throws Exception {
		List lista = new ArrayList();
		//PARTES COOMENTADAS, PARA OPTIMIZAR LA VELOCIDAD
		//A LA HORA DE METER LOS RESULTADOS EN LA QUERIE
		/*long milis = System.currentTimeMillis();
		long milisAnt = System.currentTimeMillis();
		int i=0;*/
		while (res.next()){		    
    	  W83aBaseBeanBD bean = (W83aBaseBeanBD)clase.newInstance();
	      bean.rowToBean(res,otrosValores);
/*	      if(i%100==0){
	    	  W83aClsTrazas.trazaError(null,"Query OK -> "+ (System.currentTimeMillis()-milis) + " ms - "+i + "- ant - "+((System.currentTimeMillis()-milis)-milisAnt),null);
	    	  milisAnt = System.currentTimeMillis()-milis;
	      }
	      i++;*/
	      lista.add(bean);
		}
	
		return lista;
	}


private static void meterVariableBlob(PreparedStatement st, int posicion, InputStream parameter,int size) throws SQLException{
	W83aClsTrazas.trazaDebug(null, "posicion: "+posicion+", parameter:"+parameter+", size:"+size);
		st.setBinaryStream(posicion, parameter,size);
		W83aClsTrazas.trazaDebug(null, "posicion: "+posicion+", parameter:"+parameter+", size:"+size);
}

	private static void meterVariableSql(PreparedStatement st, int posicion, Object parameter) throws SQLException{
		
		if(W83aClsConstantes.NULL_DOUBLE.equals(parameter.toString())){
			st.setNull(posicion, Types.DOUBLE);
		}else
		if(W83aClsConstantes.NULL_STRING.equals(parameter.toString())){
			st.setNull(posicion, Types.VARCHAR);
		}else
		if(W83aClsConstantes.NULL_INTEGER.equals(parameter.toString())){
			st.setNull(posicion, Types.INTEGER);
		}else
		if(W83aClsConstantes.NULL_FLOAT.equals(parameter.toString())){
			st.setNull(posicion, Types.FLOAT);
		}else
		if(W83aClsConstantes.NULL_DATE.equals(parameter.toString())){
			st.setNull(posicion, Types.DATE);
		}else
		if(W83aClsConstantes.NULL_BLOB.equals(parameter.toString())){
			st.setNull(posicion, Types.BLOB);
		}else
		
		if(StringUtils.isNotEmpty(parameter.toString())){
			st.setObject(posicion, parameter);
		}
	}
	
	/**
	 * Ejecuta un movimiento en la BD.
	 * Usaremos en la sql para poner las variables '?' y meter en 'parameters' los valores correspondientes
	 * La función posteriormente usa meterVariableSql, para insertar estos valores en la sql según el tipo que
	 *  sean. 
	 *  Si el valor que se desea usar para insertarlo en la tabla es nulo en vez de null usar los valores
	 *  de las constantes habilitado para ello. (NULL_DOUBLE, NULL_STRING,...)	 * 
	 *
	 * @param con the con
	 * @param sql the sql
	 * @param parameters the parameters
	 * @return the int
	 * @throws SQLException the sQL exception
	 * @throws NamingException 
	 */
	public static int executeTableMovement(String sql, List parameters,W83aUsuarioXLNetsBean usuarioBean) throws SQLException, NamingException {
		
		Connection con = null; //NOPMD
		
		con = getConnection();
	    PreparedStatement st=con.prepareStatement(sql);
		
		W83aClsTrazas.traza(usuarioBean,"PreparedStatement creado: " + sql);
		  if (parameters!=null && parameters.size()>0){
			  for (int i=0;i<parameters.size();i++){
				  meterVariableSql(st,i+1,parameters.get(i));
				  W83aClsTrazas.traza(usuarioBean,"parametro " + (i+1) + ": " + parameters.get(i));
			  }
		  }
		  int resultadoInt = st.executeUpdate();
		  st.close();
		  con.close();
		  return resultadoInt;
	}
	
	
	/**
	 * Ejecuta un movimiento en la BD.
	 * Usaremos en la sql para poner las variables '?' y meter en 'parameters' los valores correspondientes
	 * La función posteriormente usa meterVariableSql, para insertar estos valores en la sql según el tipo que
	 *  sean. 
	 *  Si el valor que se desea usar para insertarlo en la tabla es nulo en vez de null usar los valores
	 *  de las constantes habilitado para ello. (NULL_DOUBLE, NULL_STRING,...)	 * 
	 *
	 * @param con the con
	 * @param sql the sql
	 * @param parameters the parameters
	 * @return the int
	 * @throws SQLException the sQL exception
	 * @throws NamingException 
	 */
	public static int executeTableMovementDeleg(String sql, List parameters,W83aUsuarioXLNetsBean usuarioBean) throws SQLException, NamingException {
		
		Connection con = null; //NOPMD
		con = getConnection();
	    PreparedStatement st=con.prepareStatement(sql);
	 
	    //IDENTIFICAR USUARIO
		identificarUsuarioAuditor(usuarioBean.getIdUsuario());
		
		W83aClsTrazas.traza(usuarioBean,"PreparedStatement creado: " + sql);
		  if (parameters!=null && parameters.size()>0){
			  for (int i=0;i<parameters.size();i++){
				 
				  meterVariableSql(st,i+1,parameters.get(i));
				  W83aClsTrazas.traza(usuarioBean,"parametro " + (i+1) + ": " + parameters.get(i));
			  }
		  }
		  int resultadoInt = st.executeUpdate();
		  
		  //TERMINAR USUARIO
		  W83aDataBase.terminarUsuarioAuditor();
			
			
		  st.close();
		  con.close();
		  return resultadoInt;
	}
	
public static int executeBlobTableMovement(String sql, List parameters,int size,W83aUsuarioXLNetsBean usuarioBean) throws SQLException, NamingException {
		
		Connection con = null;//NOPMD
		con = getConnection();
	    PreparedStatement st=con.prepareStatement(sql);
		
		W83aClsTrazas.traza(usuarioBean,"PreparedStatement creado: " + sql);
		  if (parameters!=null && parameters.size()>0){
			  for (int i=0;i<parameters.size();i++){
				  
				  if(null!=parameters.get(i)){
					  if (parameters.get(i) instanceof InputStream)
					  {
						  meterVariableBlob (st,i+1,(InputStream)parameters.get(i),size);
						  W83aClsTrazas.traza(usuarioBean,"parametroB " + (i+1) + ": " + parameters.get(i));
						  continue;
					  }
				  }
				  meterVariableSql(st,i+1,parameters.get(i));
				  W83aClsTrazas.traza(usuarioBean,"parametro " + (i+1) + ": " + parameters.get(i));
			  }
		  }
		   int resul=st.executeUpdate();
		  st.close();
		  con.close();
		  return resul;
	}




	
	/*
	 * Funciones para sacar del resultSet el valor de una columna
	 */
	public static String getStringFromRS(ResultSet rs, String column){
		try {
			return rs.getString(column);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			return "";
		}
	}	
	public static int getIntegerFromRS(ResultSet rs, String column){
		try {
			return rs.getInt(column);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			return -1;
		}
	}
	public static BigDecimal getBigDecimalFromRS(ResultSet rs, String column){
		try {
			return rs.getBigDecimal(column);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			return new BigDecimal(0);
		}
	}
	public static Blob getBlobFromRS(ResultSet rs, String column){
		try {
			return rs.getBlob(column);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			return null;
		}
	}
	public static Date getDateFromRS(ResultSet rs, String column){
		try {
			return rs.getDate(column);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			return null;
		}
	}
	public static double getDoubleFromRS(ResultSet rs, String column){
		try {
			return rs.getDouble(column);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			return 0;
		}
	}
	public static void identificarUsuarioAuditor(String idUsuario) throws SQLException, NamingException
	{
		String strEntornoAplic=Q70ListenerUtils.getApplicationProperty("ENTORNO_W83B");
		
		if(!"LOCAL".equals(strEntornoAplic)){
			CallableStatement cs;
		    cs = W83aDataBase.getConnection().prepareCall("{CALL SYSAUDITOR.IDENTIFICA_USUARIO(?)}");
		    cs.setObject(1, idUsuario);
		    cs.execute();
		    cs.close();
		 }	  
	}
	
	public static void terminarUsuarioAuditor() throws SQLException, NamingException{
	
		String strEntornoAplic=Q70ListenerUtils.getApplicationProperty("ENTORNO_W83B");
		if(!"LOCAL".equals(strEntornoAplic)){
			CallableStatement cs = W83aDataBase.getConnection().prepareCall("{CALL SYSAUDITOR.TERMINA_USUARIO()}");
			cs.execute();
			cs.close();
		}
    
	}
	
}